package org.setec.utils.io;

import org.setec.utils.Utils;

import java.io.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;

public class Persistance {

    public static void learn(String storage, Stub stub){
        File stubFile = new File(calculateStubPath(storage, stub));
        ensureDirExists(stubFile);
        stubFile.delete();
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(stubFile));
            objectOutputStream.writeObject(stub);
            objectOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Stub recall(String storage, String className, String methodName, Object[] args){
        File stubFile = new File(calculateStubPath(storage, className, methodName, args));
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(stubFile));
            Stub stub = (Stub)objectInputStream.readObject();
            objectInputStream.close();
            return stub;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void removeStub(String storage, Stub stub){
        File stubFile = new File(calculateStubPath(storage, stub));
        ensureDirExists(stubFile);
        stubFile.delete();
    }

    public static boolean persists(String storage, String className, String methodName, Object[] args){
        File stubFile = new File(calculateStubPath(storage, className, methodName, args));
        return stubFile.exists() && stubFile.isFile();
    }

    private static String calculateStubPath(String storage, String className, String methodName, Object[] args) {
        String separator = System.getProperty("file.separator");
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(storage).append(separator).
                append(className.replaceAll("\\.", "_")).append("-").
                append(methodName).append("-");

        for (Iterator it = Arrays.asList(Utils.objectClasses(args)).iterator(); it.hasNext(); ) {
            Class clazz = (Class) it.next();
            stringBuffer.append(clazz.getName().replaceAll(".*\\.", "_"));
        }

        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(new Object[] { className, methodName, args });
            byte[] bytes = byteArrayOutputStream.toByteArray();
            String md5 = Utils.getMD5Hash(bytes);
            stringBuffer.append("-");
            stringBuffer.append(md5);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuffer.toString();
    }

    private static String calculateStubPath(String storage, Stub stub){
        return calculateStubPath(storage, stub.getClassName(), stub.getMethodName(), stub.getArgs());
    }

    public static Stub loadStub(String path){
        File stubFile = new File(path);
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(stubFile));
            Stub stub = (Stub)objectInputStream.readObject();
            objectInputStream.close();
            return stub;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void ensureDirExists(File file){
        if( !file.getParentFile().exists() ){
            file.getParentFile().mkdirs();
        }
    }

    
    


}
